﻿Imports System.Data.Odbc
Imports AutoCAD

''' <summary>
''' 工件服务实现
''' </summary>
Public Class CadPartServiceImpl
    Implements ICadPartService

    Private cadEntityDataService As ICadEntityDataService = New CadEntityDataServiceImpl
    Private cadApplicationService As ICadApplicationService = New CadApplicationServiceImpl
    Private cadBlockReferenceService As ICadBlockReferenceService = New CadBlockReferenceServiceImpl



    ''' <summary>
    ''' 转换成工件
    ''' 【注意】工件必须为块引用且存于‘工件’图层,g工件必须为块引用,存与'工件'图层，其他均视为代表加工的图元
    ''' </summary>
    ''' <param name="doc"></param>
    ''' <param name="ents"></param>
    Public Sub TranslateToPart(ByRef doc As AcadDocument, ByRef ents As IEnumerable) Implements ICadPartService.TranslateToPart

        ' 1.获取图元数据
        Dim dt_entityData As DataTable = cadEntityDataService.GetEntiyData(ents) '获取图元数据

        ' 2.图元数据分组, 分为板件与加工数据
        Dim dt_plate As DataTable = dt_entityData.Clone ' 工件图元
        Dim dt_other As DataTable = dt_entityData.Clone ' 其他图元
        For Each dr As DataRow In dt_entityData.Rows
            If cadEntityDataService.IsPlate(dr("layer"), dr("objectName")) Then ' 板件
                dt_plate.Rows.Add(dr.ItemArray)
            ElseIf Not cadEntityDataService.IsPart(dr("layer"), dr("objectName")) Then ' 非工件且非板件则视为加工
                dt_other.Rows.Add(dr.ItemArray)
            End If
        Next dr

        ' 3.根据板件与加工图元的位置关系决定隶属, 并将其规整构建'工件'块
        Dim zeroPoint() As Double = CadApplicationServiceImpl.ZeroPoint '世界坐标系零点位置
        Dim works As New List(Of AcadEntity)
        For Each dr_plate As DataRow In dt_plate.Rows '遍历'板件'
            Dim plate As AcadBlockReference = cadApplicationService.ObjectIdToObject(doc, dr_plate("ObjectId")) '代表板件的图元
            works.Add(plate) ' 将板件添加集合中

            '3.1. 记录板件位置/法向/角度, 用于工件替换板件
            Dim platePosition As Double() = plate.InsertionPoint
            Dim plateNormal As Double() = plate.Normal
            Dim plateRotation As Double = plate.Rotation

            ' 3.2. 通过板件包围盒与加工图元的质心判断范围, 将符合条件的加工图元添加到works
            Dim filterStr As String = $"centroidX >= {dr_plate("minpX")} and centroidY >= {dr_plate("minpY")} and centroidZ >= {dr_plate("minpZ")} and centroidX <= {dr_plate("maxpX")} and centroidY <= {dr_plate("maxpY")} and centroidZ <= {dr_plate("maxpZ")} "
            For Each dr_work As DataRow In dt_other.Select(filterStr) '将隶属板件的加工图元添加到集合
                Dim work As AcadEntity = cadApplicationService.ObjectIdToObject(doc, dr_work("ObjectId")) ' 代表加工的图元
                works.Add(work)
                dr_work.Delete()
            Next dr_work

            ' 3.3. 将 works 设置为平铺状态, 所包含表示加工的图元对象与板件之间保持原始位置关系
            WcsToOcs(works.ToArray, plate) ' 

            ' 3.4. 遍历 works 移动到0,0点位置
            Dim originalPosition() As Double = plate.InsertionPoint ' 板件块的插入点位置
            For Each work In works
                work.Move(originalPosition, zeroPoint)
            Next work

            ' 3.5. 将 works 集合构建块
            Dim partB As AcadBlock = buildPartBlock(doc, plate.Handle, works.ToArray)

            ' 3.6. 将块插入到 plate 原位置, 并设置为相同角度
            Dim partBR As AcadBlockReference = doc.ModelSpace.InsertBlock(CadApplicationServiceImpl.ZeroPoint, partB.Name, 1, 1, 1, 0)
            With partBR
                .Normal = plateNormal
                .InsertionPoint = platePosition
                .Rotation = plateRotation
                .Layer = "工件"
                .Update()
            End With

            ' 3.7. 删除原始对象并清空集合
            For Each ent In works
                ent.Delete()
            Next ent
            works.Clear() ' 清空集合

        Next dr_plate

    End Sub





    ' 构建工件块, 并将图元集合添加到块中
    Function buildPartBlock(ByRef doc As AcadDocument, ByRef blockName As String, entitys As AcadEntity()) As AcadBlock
        Dim b As AcadBlock = doc.Blocks.Add(New Double() {0, 0, 0}, blockName)
        doc.CopyObjects(entitys.ToArray, b)
        Return b
    End Function

    ' 将板件平放, 并将加工内容适应板件的ocs
    Sub WcsToOcs(ByRef partSubEntity As AcadEntity(), ByRef plate As AcadBlockReference)

        Dim doc As AcadDocument = plate.Document
        Dim plateNormal As Double() = plate.Normal ' 板件法线
        Dim plateRotation As Double = plate.Rotation ' 板件旋转角度
        Dim platePosition As Double = plate.InsUnitsFactor

        Dim entOcsPosition As Double() '记录集合中对象ocs位置
        Dim centroid() As Double '计算集合对象的质心
        ' 遍历
        For Each ent As AcadEntity In partSubEntity
            With doc.Utility
                ' 推算在板件的ocs坐标系下ent的位置
                centroid = cadEntityDataService.GetCentroid(ent) ' 获得图元质心, 用来表示图元对象相对于板件的位置
                entOcsPosition = .TranslateCoordinates(centroid, AcCoordinateSystem.acWorld, AcCoordinateSystem.acOCS, False, plateNormal) ' 将位置转换为ocs坐标位置

                ' 推算在板件的ocs坐标系下ent的法向量, 替换原法向, 此动作执行后ent的位置会发生改变, 所以需要重新获取质心位置作为移动的起始点
                ent.normal = .TranslateCoordinates(ent.normal, AcCoordinateSystem.acWorld, AcCoordinateSystem.acOCS, False, plateNormal) '当修改新的法向, 位置会发生改变, 所以下面需要重新获取质心位置
            End With

            ' 移动ent从当 前质心位置 移动到 entOcsPosition
            centroid = cadEntityDataService.GetCentroid(ent) ' 重新获得图元质心, 用来表示图元对象当前wcs位置
            ent.Move(centroid, entOcsPosition)

            ' 旋转
            ent.Rotate(CadApplicationServiceImpl.ZeroPoint, -plateRotation)
        Next

    End Sub






    ''' <summary>
    ''' 拷贝工件并展平
    ''' </summary>
    ''' <param name="doc"></param>
    ''' <param name="parts"></param>
    Public Sub CopyAndExpandPart(ByRef doc As AcadDocument, ByRef parts As IEnumerable) Implements ICadPartService.CopyAndExpandPart
        doc.Activate()
        Dim position() As Double = doc.Utility.GetPoint(CadApplicationServiceImpl.ZeroPoint, "拾取位置")
        Dim normal() As Double = New Double() {0, 0, 1}
        For Each ent As AcadEntity In parts
            If cadEntityDataService.IsPart(ent) Then
                Dim br As AcadBlockReference = ent.Copy
                With br
                    .Normal = normal
                    .Rotation = 0

                    Dim size() As Double = cadEntityDataService.GetSize(ent)
                    .InsertionPoint = position
                    .Update()
                    position(0) += size(0) + 1000

                End With
            End If
        Next ent
    End Sub




    ''' <summary>
    ''' 创建三视图
    ''' </summary>
    ''' <param name="doc"></param>
    ''' <param name="parts"></param>
    Public Sub CreateThreeViews(ByRef doc As AcadDocument, ByRef parts As IEnumerable) Implements ICadPartService.CreateThreeViews
        For Each ent As AcadEntity In parts
            If cadEntityDataService.IsPart(ent) Then
                CreateLeftView(ent)
                CreateFrontView(ent)
            End If
        Next
    End Sub

    ''' <summary>
    ''' 创建左视图
    ''' </summary>
    ''' <param name="br"></param>
    Sub CreateLeftView(ByRef br As AcadBlockReference)
        Dim size() As Double = cadEntityDataService.GetSize(br)
        Dim position() As Double = br.InsertionPoint
        position(0) += size(0) + 100
        Dim copyBr As AcadBlockReference = br.Copy
        With copyBr
            .Layer = "工件"
            .InsertionPoint = position
            .Rotate3D(position, New Double() {position(0), position(1) + 1, position(2)}, Math.PI / 2)
        End With
    End Sub

    ''' <summary>
    ''' 创建前视图
    ''' </summary>
    ''' <param name="br"></param>
    Private Sub CreateFrontView(ByRef br As AcadBlockReference)
        Dim size() As Double = cadEntityDataService.GetSize(br)
        Dim position() As Double = br.InsertionPoint
        position(1) += size(1) + 100
        Dim copyBr As AcadBlockReference = br.Copy
        With copyBr
            .Layer = "工件"
            .InsertionPoint = position
            .Rotate3D(position, New Double() {position(0) - 1, position(1), position(2)}, Math.PI / 2)
        End With
    End Sub

    ''' <summary>
    ''' 构建工件数据表
    ''' </summary>
    ''' <returns></returns>
    Public Function BuildPartDataTable() As DataTable Implements ICadPartService.BuildPartDataTable
        Return cadEntityDataService.BuildDataTable_PartData
    End Function


End Class
